﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Response.cs" company="Rolosoft Ltd">
//   © Rolosoft Ltd
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

#region License

// Copyright 2013 Rolosoft Ltd
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion

namespace Rsft.Net.Dns.Entities
{
    #region Usings

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Diagnostics.Contracts;
    using System.Net;

    using Rsft.Net.Dns.Entities.Records;
    using Rsft.Net.Dns.Interfaces;

    #endregion

    /// <summary>
    ///     The response.
    /// </summary>
    public sealed class Response
    {
        #region Constants

        /// <summary>
        ///     The default cache size.
        /// </summary>
        private const int DefaultSize = 1024;

        #endregion

        #region Fields

        /// <summary>
        ///     The cache size.
        /// </summary>
        private readonly int size;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="Response"/> class.
        /// </summary>
        /// <param name="size">
        /// The size.
        /// </param>
        private Response(int size)
        {
            this.size = size;

            this.Questions = new List<Question>();
            this.Answers = new List<AnswerResourceRecord>();
            this.Authorities = new Collection<AuthorityResourceRecord>();
            this.Additionals = new Collection<AdditionalResourceRecord>();

            this.Server = new IPEndPoint(0, 0);
            this.Error = string.Empty;
            this.MessageSize = 0;
            this.TimeStamp = DateTime.Now;
            this.Header = new Header();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Response" /> class.
        /// </summary>
        /// <param name="recordReaderFactory">The record reader factory.</param>
        /// <param name="ipendpoint">The IP endpoint.</param>
        /// <param name="data">The data.</param>
        /// <param name="size">The size.</param>
        public Response(IRecordReaderFactory recordReaderFactory, IPEndPoint ipendpoint, byte[] data, int size = DefaultSize)
        {
            Contract.Requires(recordReaderFactory != null);
            Contract.Requires(ipendpoint != null);
            Contract.Requires(data != null);

            var recordReader = recordReaderFactory.Create(data);

            this.size = size;

            this.Error = string.Empty;
            this.Server = ipendpoint;
            this.TimeStamp = DateTime.Now;
            this.MessageSize = data.Length;
            

            this.Questions = new List<Question>();
            this.Answers = new List<AnswerResourceRecord>();
            this.Authorities = new Collection<AuthorityResourceRecord>();
            this.Additionals = new Collection<AdditionalResourceRecord>();

            this.Header = new Header(recordReader);

            for (var intI = 0; intI < this.Header.QdCount; intI++)
            {
                this.Questions.Add(new Question(recordReader));
            }

            for (var intI = 0; intI < this.Header.Ancount; intI++)
            {
                this.Answers.Add(new AnswerResourceRecord(recordReader));
            }

            for (var intI = 0; intI < this.Header.Nscount; intI++)
            {
                this.Authorities.Add(new AuthorityResourceRecord(recordReader));
            }

            for (var intI = 0; intI < this.Header.Arcount; intI++)
            {
                this.Additionals.Add(new AdditionalResourceRecord(recordReader));
            }
        }

        /// <summary>
        ///     Initializes a new instance of the <see cref="Response" /> class.
        /// </summary>
        public Response()
            : this(DefaultSize)
        {
        }

        #endregion

        #region Public Properties

        /// <summary>
        ///     Gets the additionals.
        /// </summary>
        /// <value>The additionals.</value>
        public ICollection<AdditionalResourceRecord> Additionals { get; private set; }

        /// <summary>
        ///     Gets the answers.
        /// </summary>
        /// <value>The answers.</value>
        public IList<AnswerResourceRecord> Answers { get; private set; }

        /// <summary>
        ///     Gets the authorities.
        /// </summary>
        /// <value>The authorities.</value>
        public ICollection<AuthorityResourceRecord> Authorities { get; private set; }

        /// <summary>
        ///     Gets the Bytes Used.
        /// </summary>
        /// <value></value>
        public int BytesUsed
        {
            get
            {
                return this.size;
            }
        }

        /// <summary>
        ///     Gets or sets the error.
        /// </summary>
        /// <value>The error.</value>
        public string Error { get; set; }

        /// <summary>
        ///     Gets the header.
        /// </summary>
        /// <value>The header.</value>
        public Header Header { get; private set; }

        /// <summary>
        ///     Gets or sets the size of the message.
        /// </summary>
        /// <value>The size of the message.</value>
        public int MessageSize { get; set; }

        /// <summary>
        ///     Gets the questions.
        /// </summary>
        /// <value>The questions.</value>
        public IList<Question> Questions { get; private set; }

        /// <summary>
        ///     Gets the A records.
        /// </summary>
        /// <value>The records A.</value>
        public IList<A> RecordsA
        {
            get
            {
                var list = new List<A>();
                foreach (AnswerResourceRecord answer in this.Answers)
                {
                    var record = answer.Record as A;
                    if (record != null)
                    {
                        list.Add(record);
                    }
                }

                return list;
            }
        }

        /// <summary>
        ///     Gets the records AAAA.
        /// </summary>
        /// <value>The records AAAA.</value>
        public ICollection<AAAA> RecordsAAAA
        {
            get
            {
                var list = new Collection<AAAA>();
                foreach (AnswerResourceRecord answer in this.Answers)
                {
                    var record = answer.Record as AAAA;
                    if (record != null)
                    {
                        list.Add(record);
                    }
                }

                return list;
            }
        }

        /// <summary>
        ///     Gets the records CNAME.
        /// </summary>
        /// <value>The records CNAME.</value>
        public ICollection<CNAME> RecordsCNAME
        {
            get
            {
                var list = new Collection<CNAME>();
                foreach (AnswerResourceRecord answer in this.Answers)
                {
                    var record = answer.Record as CNAME;
                    if (record != null)
                    {
                        list.Add(record);
                    }
                }

                return list;
            }
        }

        /// <summary>
        ///     Gets the records MX.
        /// </summary>
        /// <value>The records MX.</value>
        public ICollection<MX> RecordsMX
        {
            get
            {
                var list = new Collection<MX>();
                foreach (AnswerResourceRecord answer in this.Answers)
                {
                    var record = answer.Record as MX;
                    if (record != null)
                    {
                        list.Add(record);
                    }
                }

                return list;
            }
        }

        /// <summary>
        ///     Gets the records NS.
        /// </summary>
        /// <value>The records NS.</value>
        public ICollection<NS> RecordsNS
        {
            get
            {
                var list = new Collection<NS>();
                foreach (AnswerResourceRecord answer in this.Answers)
                {
                    var record = answer.Record as NS;
                    if (record != null)
                    {
                        list.Add(record);
                    }
                }

                return list;
            }
        }

        /// <summary>
        ///     Gets the records PTR.
        /// </summary>
        /// <value>The records PTR.</value>
        public IList<PTR> RecordsPtr
        {
            get
            {
                var list = new Collection<PTR>();
                foreach (AnswerResourceRecord answer in this.Answers)
                {
                    var record = answer.Record as PTR;
                    if (record != null)
                    {
                        list.Add(record);
                    }
                }

                return list;
            }
        }

        /// <summary>
        /// Gets the records resource record.
        /// </summary>
        /// <value>
        /// The records resource record.
        /// </value>
        public ICollection<ResourceRecord> RecordsResourceRecord
        {
            get
            {
                var list = new Collection<ResourceRecord>();

                foreach (AnswerResourceRecord rr in this.Answers)
                {
                    list.Add(rr);
                }

                foreach (AuthorityResourceRecord rr in this.Authorities)
                {
                    list.Add(rr);
                }

                foreach (AdditionalResourceRecord rr in this.Additionals)
                {
                    list.Add(rr);
                }

                return list;
            }
        }

        /// <summary>
        ///     Gets the records SOA.
        /// </summary>
        /// <value>The records SOA.</value>
        public ICollection<SOA> RecordsSOA
        {
            get
            {
                var list = new Collection<SOA>();
                foreach (AnswerResourceRecord answer in this.Answers)
                {
                    var record = answer.Record as SOA;
                    if (record != null)
                    {
                        list.Add(record);
                    }
                }

                return list;
            }
        }

        /// <summary>
        ///     Gets the records TXT.
        /// </summary>
        /// <value>The records TXT.</value>
        public ICollection<TXT> RecordsTXT
        {
            get
            {
                var list = new Collection<TXT>();
                foreach (AnswerResourceRecord answer in this.Answers)
                {
                    var record = answer.Record as TXT;
                    if (record != null)
                    {
                        list.Add(record);
                    }
                }

                return list;
            }
        }

        /// <summary>
        ///     Gets or sets the server.
        /// </summary>
        /// <value>The server.</value>
        public IPEndPoint Server { get; set; }

        /// <summary>
        ///     Gets the time stamp.
        /// </summary>
        /// <value>The time stamp.</value>
        public DateTime TimeStamp { get; private set; }

        #endregion
    }
}